﻿namespace Arms.Framework
{
    using System;
    using System.Collections.Generic;
    using System.Text;

    public static class StringExtensions
    {
        /// <summary>
        /// Appends a string value.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string Append(this string source, string value)
        {
            return source.Concatenate(value);
        }
        /// <summary>
        /// Concatenates two strings together without a delimiter
        /// </summary>
        /// <param name="source"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string Concatenate(this string source, string value)
        {
            return source.Concatenate(value, string.Empty);
        }
        /// <summary>
        /// Concatenates two strings together with a delimiter in between
        /// </summary>
        /// <param name="source"></param>
        /// <param name="value"></param>
        /// <param name="delimiter"></param>
        /// <returns></returns>
        public static string Concatenate(this string source, string value, string delimiter)
        {
            return string.Format("{0}{1}{2}", source, delimiter, value);
        }
        /// <summary>
        /// Determines whether a string contains any supplied values
        /// </summary>
        /// <param name="value"></param>
        /// <param name="values">The values to compare with</param>
        /// <returns></returns>
        public static bool ContainsAny(this string source, params string[] values)
        {
            foreach (string value in values)
            {
                if (source.Contains(value))
                {
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Determines whether this string contains a specified length of the specified string
        /// </summary>
        /// <param name="source"></param>
        /// <param name="value"></param>
        /// <param name="maxAllowableLength"></param>
        /// <returns></returns>
        public static bool ContainsAny(this string source, string value, int maxAllowableLength)
        {
            string[] values = new string[value.Length - maxAllowableLength + 1];
            for (int i = 0; i <= value.Length - maxAllowableLength; i++)
            {
                values[i] = value.Substring(i, maxAllowableLength);
            }
            return source.ContainsAny(values);
        }
        /// <summary>
        /// Determines whether this string has any case letter
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static bool HasLowerCaseLetter(this string source)
        {
            for (int i = 0; i < source.Length; i++)
            {
                if (char.IsLower(source[i]))
                {
                    return true;
                }
            }

            return false;
        }
        /// <summary>
        /// Determines whether this string contains special characters
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static bool HasNonAlphanumeric(this string source)
        {
            for (int i = 0; i < source.Length; i++)
            {
                if (!char.IsLetterOrDigit(source[i]))
                {
                    return true;
                }
            }

            return false;
        }
        /// <summary>
        /// Determines whether this string contains digits
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static bool HasNumber(this string source)
        {
            for (int i = 0; i < source.Length; i++)
            {
                if (char.IsDigit(source[i]))
                {
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Determines whether this string contains upper case leters
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool HasUpperCaseLetter(this string value)
        {
            for (int i = 0; i < value.Length; i++)
            {
                if (char.IsUpper(value[i]))
                {
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Inserts a delimiter before each capital letter in this string
        /// </summary>
        /// <param name="source"></param>
        /// <param name="delimiter"></param>
        /// <returns></returns>
        public static string InsertBeforeCapitalizations(this string source, string delimiter = " ")
        {
            if (string.IsNullOrEmpty(source))
            {
                return string.Empty;
            }

            StringBuilder newText = new StringBuilder(source.Length * 2);

            for (int i = 0; i < source.Length; i++)
            {
                if (char.IsUpper(source[i]))
                {
                    if (((i < source.Length - 1) && !char.IsUpper(source[i + 1])) || ((i > 0) && !char.IsUpper(source[i - 1])))
                    {
                        if (newText.Length > 0)
                        {
                            newText.Append(delimiter);
                        }
                    }
                }

                newText.Append(source[i]);
            }

            return newText.ToString().Trim();
        }
        /// <summary>
        /// Determines whether this string is equal to another string
        /// </summary>
        /// <param name="source"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsEqual(this string source, string value, bool ignoreCase = false)
        {
            if (ignoreCase)
            {
                return source.Equals(value, StringComparison.CurrentCultureIgnoreCase);
            }

            return source.Equals(value, StringComparison.CurrentCulture);
        }
        /// <summary>
        /// Inserts white space before each capital letter in this string
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string InsertWhiteSpaceBeforeCapitalizations(this string source)
        {
            return source.InsertBeforeCapitalizations();
        }
        /// <summary>
        /// Determines whether this string is null or empty.
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static bool IsNullOrEmpty(this string source)
        {
            return string.IsNullOrEmpty(source);
        }
        /// <summary>
        /// Prepends a string value.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string Prepend(this string source, string value)
        {
            return value.Concatenate(source);
        }
        /// <summary>
        /// Removes all white spaces
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string RemoveWhiteSpace(this string source)
        {
            return source.RemoveAllInstancesOf(" ");
        }
        /// <summary>
        /// Removes all numbers from this string
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string RemoveNumbers(this string source)
        {
            string newString = string.Empty;
            foreach (char c in source)
            {
                if (!Char.IsDigit(c))
                {
                    newString += c;
                }
            }
            return newString;
        }
        /// <summary>
        /// Removes all special characters from this string
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string RemoveSpecials(this string source)
        {
            string newString = string.Empty;
            foreach (char c in source)
            {
                if (!Char.IsSymbol(c) && !Char.IsPunctuation(c) && !Char.IsControl(c))
                {
                    newString += c;
                }
            }
            return newString;
        }
        /// <summary>
        /// Removes all letters from this string
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string RemoveLetters(this string source)
        {
            string newString = string.Empty;
            foreach (char c in source)
            {
                if (!Char.IsLetter(c))
                {
                    newString += c;
                }
            }
            return newString;
        }
        /// <summary>
        /// Remove all values found in this string
        /// </summary>
        /// <param name="source"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        public static string RemoveAllInstancesOf(this string source, params string[] values)
        {
            foreach (string value in values)
            {
                source = source.Replace(value, string.Empty);
            }
            return source;
        }
        /// <summary>
        /// Replaces a value in this string with a new value
        /// </summary>
        /// <param name="source"></param>
        /// <param name="oldValue"></param>
        /// <param name="newValue"></param>
        /// <param name="startIndex"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static string Replace(this string source, string oldValue, string newValue, int startIndex, int length)
        {
            string beginning = source.Substring(0, startIndex);
            string middle = source.Substring(startIndex, length);
            string end = source.Substring(startIndex + length);

            middle = middle.Replace(oldValue, newValue);

            return string.Format("{0}{1}{2}", beginning, middle, end);
        }
        /// <summary>
        /// Replaces the last instance of a value in this string with a new value
        /// </summary>
        /// <param name="source"></param>
        /// <param name="oldValue"></param>
        /// <param name="newValue"></param>
        /// <returns></returns>
        public static string ReplaceLastInstanceOf(this string source, string oldValue, string newValue)
        {
            int start = source.LastIndexOf(oldValue);
            int length = oldValue.Length;

            if (start >= 0)
            {
                return source.Replace(oldValue, newValue, start, length);
            }
            else
            {
                return source;
            }
        }
        /// <summary>
        /// Replaces all supplied values in this string with a new value
        /// </summary>
        /// <param name="source"></param>
        /// <param name="newValue">The string to replace with</param>
        /// <param name="oldValues">The strings to replace</param>
        /// <returns></returns>
        public static string ReplaceWith(this string source, string newValue, params string[] oldValues)
        {
            foreach (string oldValue in oldValues)
            {
                source = source.Replace(oldValue, newValue);
            }
            return source;
        }
        /// <summary>
        /// Shifts this string to the left
        /// </summary>
        /// <param name="source"></param>
        /// <param name="appendText"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static string ShiftToLeft(this string source, string appendText, int length)
        {
            string temp = string.Format("{0}{1}", source, appendText);
            int len = temp.Length;
            return temp.Substring(0, length);
        }
        /// <summary>
        /// Shifts this string to the right
        /// </summary>
        /// <param name="source"></param>
        /// <param name="appendText"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static string ShiftToRight(this string source, string appendText, int length)
        {
            string temp = string.Format("{0}{1}", appendText, source);
            int len = temp.Length;
            return temp.Substring(len - length, length);
        }
        /// <summary>
        /// Splits this string. Comma delimited only
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static List<T> Split<T>(this string source)
        {
            return source.Split<T>(StringSplitOptions.None);
        }
        /// <summary>
        /// Splits this string with specified delimiters
        /// </summary>
        /// <param name="source"></param>
        /// <param name="delimiters"></param>
        /// <returns></returns>
        public static string[] Split(this string source, params string[] delimiters)
        {
            return source.Split(delimiters, StringSplitOptions.None);
        }
        /// <summary>
        /// Splits this string with specified delimiters
        /// </summary>
        /// <param name="source"></param>
        /// <param name="option"></param>
        /// <param name="delimiters"></param>
        /// <returns></returns>
        public static string[] Split(this string source, StringSplitOptions option, params string[] delimiters)
        {
            return source.Split(delimiters, option);
        }
        /// <summary>
        /// Splits this string. Comma delimited only
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="option"></param>
        /// <returns>Generic List</returns>
        public static List<T> Split<T>(this string source, StringSplitOptions option)
        {
            char[] delimiter = { ',' };
            return source.Split<T>(option, delimiter);
        }
        /// <summary>
        /// Splits this string. Comma delimited only
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="options"></param>
        /// <param name="delimiters"></param>
        /// <returns>Generic List</returns>
        public static List<T> Split<T>(this string source, StringSplitOptions options, params char[] delimiters)
        {
            if (!string.IsNullOrEmpty(source))
            {
                string[] splitStrings = source.ToNullableString().Split(delimiters, options);

                List<T> listT = new List<T>();

                foreach (string splitString in splitStrings)
                {
                    if (!string.IsNullOrWhiteSpace(splitString))
                    {
                        Type typeT = typeof(T);
                        if (typeT.IsEnum)
                        {
                            listT.Add((T)Enum.Parse(typeT, splitString));
                        }
                        else
                        {
                            try
                            {
                                listT.Add((T)Convert.ChangeType(splitString, typeof(T)));
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine(ex.ToString());
                            }
                        }
                    }
                    else
                    {
                        listT.Add(default(T));
                    }
                }

                return listT;
            }
            else
            {
                return new List<T>();
            }
        }
        /// <summary>
        /// Split this string at each capital letter
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string[] SplitAtCapitalizations(this string source)
        {
            return source.InsertBeforeCapitalizations("|").Split('|');
        }
        /// <summary>
        /// Determines whether this string starts with specified values
        /// </summary>
        /// <param name="source"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        public static bool StartsWith(this string source, params string[] values)
        {
            foreach (var value in values)
            {
                if (source != null && source.StartsWith(value))
                {
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Determines whether this string starts with specified values. Removes values if match is found
        /// </summary>
        /// <param name="source"></param>
        /// <param name="output">The resulting string</param>
        /// <param name="values"></param>
        /// <returns></returns>
        public static bool StartsWithThenRemove(this string source, out string output, params string[] values)
        {
            output = source;
            bool startsWith = false;

            if (!string.IsNullOrEmpty(source))
            {
                foreach (var value in values)
                {
                    startsWith = source.StartsWith(value);
                    if (startsWith)
                    {
                        output = source.Remove(0, value.Length);
                        break;
                    }
                }
            }

            return startsWith;
        }
        /// <summary>
        /// Cleans and trims this string to be CSV Compliant
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string ToCSVCompliantString(this string source)
        {
            return source.ReplaceWith(" ", ",", "\r\n").Replace("\"", string.Empty);
        }
        /// <summary>
        /// Cleans and trims this string to be file compliant
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string ToFileCompliantString(this string source)
        {
            return source.ReplaceWith("-", "/", "\\", "$", " ", ":", "*", "?", "\"", "<", ">", "|");
        }
        /// <summary>
        /// Converts this string into phone number format
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string ToPhoneNumberFormat(this string source)
        {
            if (source != null && source.Length == 10)
            {
                source = source.Trim();
                return string.Format("{0}-{1}-{2}", source.Substring(0, 3), source.Substring(3, 3), source.Substring(6, 4));
            }
            else
            {
                return source;
            }
        }
        /// <summary>
        /// Cleans and trims this string to be XML compliant
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string ToXMLCompliantString(this string source)
        {
            return source.ReplaceWith(string.Empty, "<", ">", "'", "\"", "/").Replace("&", "and");
        }
    }
}